package javawdw.io.resource;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javawdw.io.TextReader;
import javawdw.io.TextWriter;
import javawdw.io.simple.BufferedTextReader;
import javawdw.io.simple.BufferedTextWriter;

/**
 * 资源文件加载类,请尽量使用常见字符集文件
 * 
 * @author David
 * 
 */
public class Resource {
	/**
	 * 文件名：该属性在使用spring ioc时必需注入值
	 */
	private String fileName;
	/**
	 * 字符集：该属性在使用spring ioc时可以注入值
	 */
	private String charsetName;

	private Map<String, List<String>> keyValues;
	private List<String> keyAndComments;
	private long lastLoadTime;
	private boolean hasChange;

	/**
	 * 根据给定文件名构造Resource
	 * 
	 * @param fileName
	 *            给定文件名
	 * @throws IOException
	 *             文件不存在时抛出异常
	 */
	public Resource(String fileName) throws IOException {
		this(fileName, "UTF-8");
	}

	/**
	 * 根据给定文件名及字符集构造Resource
	 * 
	 * @param fileName
	 *            给定文件名
	 * @param charsetName
	 *            给定字符集
	 * @throws IOException
	 *             文件不存在时抛出异常
	 */
	public Resource(String fileName, String charsetName) throws IOException {
		File file = new File(fileName);
		if (!file.exists()) {
			throw new FileNotFoundException(file.getAbsolutePath());
		}
		this.fileName = fileName;
		this.charsetName = charsetName;
		this.keyAndComments = new ArrayList<String>();
		this.keyValues = new LinkedHashMap<String, List<String>>();
	}

	/**
	 * 获得给定的属性名的第一个值
	 * 
	 * @param name
	 *            给定属性名
	 * @return 返回给定属性名的第一个值
	 * @throws IOException
	 *             当资源文件不存在时抛出的异常
	 */
	public String getFirstValue(String name) throws IOException {
		return getFirstValue(name, null);
	}

	/**
	 * 获得给定的属性名的第一个值，若不存在返回值
	 * 
	 * @param name
	 *            给定属性名
	 * @param defaultValue
	 *            默认值
	 * @return 返回给定属性名的第一个值，若不存在返回值
	 * @throws IOException
	 *             当资源文件不存在时抛出的异常
	 */
	public String getFirstValue(String name, String defaultValue)
			throws IOException {
		List<String> values = getValues(name);
		return values == null || values.size() == 0 ? defaultValue : values
				.get(0);
	}

	/**
	 * 获得给定属性名的值列表
	 * 
	 * @param name
	 *            给定属性名
	 * @return 返回得到的值列表
	 * @throws IOException
	 *             当资源文件不存在时抛出的异常
	 */
	public List<String> getValues(String name) throws IOException {
		this.reLoad();
		return keyValues.get(name);
	}

	/**
	 * 为给定属性名添加一个值
	 * 
	 * @param name
	 *            给定属性名
	 * @param value
	 *            给定属性值
	 * @throws IOException
	 *             当资源文件不存在时抛出的异常
	 */
	public void addValue(String name, String value) throws IOException {
		reLoad();
		if (name != null && value != null) {
			List<String> values = keyValues.get(name);
			if (values == null) {
				values = new ArrayList<String>();
				keyValues.put(name, values);
				keyAndComments.add(name);
			}
			values.add(value);
			this.hasChange = true;
		}
	}

	/**
	 * 为给定属性名设置值
	 * 
	 * @param name
	 *            给定属性名
	 * @param value
	 *            给定属性值
	 * @throws IOException
	 *             当资源文件不存在时抛出的异常
	 */
	public void setValue(String name, String value) throws IOException {
		reLoad();
		if (name != null && value != null) {
			List<String> values = keyValues.get(name);
			if (values == null) {
				values = new ArrayList<String>();
				keyValues.put(name, values);
				keyAndComments.add(name);
			} else {
				values.clear();
			}
			values.add(value);
			this.hasChange = true;
		}
	}

	/**
	 * 为给定属性名添加设置值
	 * 
	 * @param name
	 *            给定属性名
	 * @param values
	 *            给定属性值
	 * @throws IOException
	 *             当资源文件不存在时抛出的异常
	 */
	public void setValues(String name, List<String> values) throws IOException {
		reLoad();
		if (name != null && values != null && values.size() != 0) {
			keyAndComments.add(name);
			keyValues.put(name, values);
			this.hasChange = true;
		}
	}

	/**
	 * 添加注释
	 * 
	 * @param comment
	 *            注释内容
	 * @throws IOException
	 *             当资源文件不存在时抛出的异常
	 */
	public void addComment(String comment) throws IOException {
		reLoad();
		if (isComment(comment)) {
			keyAndComments.add(comment);
			this.hasChange = true;
		}
	}

	/**
	 * 根据给定属性名删除属性
	 * 
	 * @param name
	 *            给定属性名
	 * @throws IOException
	 *             当资源文件不存在时抛出的异常
	 */
	public void remove(String name) throws IOException {
		reLoad();
		if (name != null) {
			keyValues.remove(name);
			keyAndComments.remove(name);
			this.hasChange = true;
		}
	}

	private void reLoad() throws IOException {
		File file = new File(this.fileName);
		long time = file.lastModified();
		if (time != this.lastLoadTime) {
			InputStream is = new FileInputStream(file);
			TextReader textReader = new BufferedTextReader(is);
			String temp = null;
			while ((temp = textReader.readLine()) != null) {
				temp = temp.trim();
				if (!isComment(temp) && temp.contains("=")) {
					if (temp.contains("=")) {
						String[] s = temp.split("=");
						List<String> values = keyValues.get(s[0]);
						if (values == null) {
							values = new ArrayList<String>();
							keyValues.put(s[0], values);
							keyAndComments.add(s[0]);
						}
						values.add(s[1]);
					}
				} else {
					keyAndComments.add(temp);
				}
			}
			this.lastLoadTime = time;
			this.hasChange = true;
			is.close();
			textReader.close();
		}
	}

	/**
	 * 保存资源文件
	 * 
	 * @throws IOException
	 *             文件被占用时抛出异常
	 */
	public void save() throws IOException {
		if (this.hasChange) {
			OutputStream out = null;
			out = new FileOutputStream(new File(this.fileName));
			TextWriter textWriter = new BufferedTextWriter(out,
					this.charsetName);
			int i = 0;
			for (String temp : keyAndComments) {
				String prefix = i++ == 0 ? "" : "\n";
				List<String> values = keyValues.get(temp);
				if (values == null || values.size() == 0) {
					textWriter.write(prefix + temp);
				} else {
					for (String value : values) {
						textWriter.write(prefix + temp + "=" + value);
					}
				}
			}
			textWriter.close();
			out.close();
		}
	}

	private boolean isComment(String temp) {
		return temp.startsWith("//") || temp.startsWith("#");
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	public void setCharsetName(String charsetName) {
		this.charsetName = charsetName;
	}
}